<?php
/**
 * 文件上传方法
 * @author yumeixin@star-net.cn
 * @copyright  ktvme. All Rights Reserved.
 * @version $Id: Upload.php 632 2018-05-07 06:13:40Z chendanming $
 */

namespace upload;

use curl\CurlUtil;

/**
 * Class UploadFileUtil
 * @package App\Common\Utils
 */
class Upload {

    const MAX_CHUNK_SIZE = 1048576; //分块大小 8*1024*1024

    /**
     * 使用文件路径进行上传，一次性上传完毕
     * @param string $p_strUrl
     * @param string $p_strFilePathName
     * @param string $p_strFileExt
     * @param string $p_nFileType
     * @param int $p_nFileSize
     * @param string $p_strFileName
     * @return bool|mixed
     */

    public static function uploadByFilePath($p_strUrl = "", $p_strFilePathName = "", $p_strFileExt = "", $p_nFileType = '', $p_nFileSize = 0, $p_strFileName = '') {
        $postData = [
            "file" => new \CURLFile(realpath($p_strFilePathName)),
            'chunk' => 0,
            'chunks' => 1,
            "md5value" => md5_file($p_strFilePathName),
            "fileext" => $p_strFileExt,
        ];
        if(!empty($p_strFileName)) {
            $postData["relfilename"] = $p_strFileName;
        }
        $mxRes = self::curlPost($postData, $p_strUrl);
        //TODO 上传后立即删除，不做备份
        @unlink($p_strFilePathName);
        if (!$mxRes) {
            return false;
        }
        return $mxRes;
    }

    /**
     * 使用Curl进行上传
     * @param array $p_arrData 要传递的post参数
     * @param string $p_strUrl 要上传的url地址
     * @return boolean|unknown
     */
    public static function curlPost($p_arrData, $p_strUrl) {
        $curl = CurlUtil::getInstanceByConfig(['isUserAgent' => true]);
        $mxRes = $curl->post($p_strUrl, $p_arrData);
        if ($mxRes === false) {
            return false;
        } else {
            //print_r($mxRes);
        }
        return $mxRes;
    }

    /**
     * 使用文件路径进行上传，分段上传
     * @param string $p_strUrl
     * @param string $nChunks
     * @param string $i
     * @param string $arrChunk
     * @param string $strMD5
     * @param string $p_nFileType
     * @param string $p_strFileExt
     * @param string $p_strFileName
     * @return bool|mixed
     */
    public static function uploadChunk($p_strUrl = "", $nChunks = "", $i = "", $arrChunk = "", $strMD5 = "", $p_nFileType = "", $p_strFileExt = "", $p_strFileName = "") {
        $arrPostData = array(
            "file" => new \CURLFile(realpath($arrChunk)),
            'chunk' => $i,
            'chunks' => $nChunks,
            "md5value" => $strMD5,
            "fileext" => $p_strFileExt,
        );
        if(!empty($p_strFileName)) {
            $arrPostData["relfilename"] = $p_strFileName;
        }
        $mxRes = self::curlPost($arrPostData, $p_strUrl);
        //TODO 上传后立即删除，不做备份
        if (!$mxRes) {
            return false;
        }
        return $mxRes;
    }

    /**
     * 使用文件路径进行分块上传
     * @param string $p_strUrl 目标URL
     * @param string $p_strFilePathName 文件名，包括路径
     * @param string $p_strFileExt 文件扩展名
     * @param integer $p_nFileType  文件类型，CONST类型
     * @param integer $p_nFileSize  文件大小，非必须
     * @param string $p_strFileName 真实文件名，下载时的文件名
     * @return integer||boolean 成功返回FileID，失败返回false
     */
    public static function uploadChunkByFilePath($p_strUrl = "", $p_strFilePathName = "", $p_strFileExt, $p_nFileType, $p_nFileSize = 0, $p_strFileName = '') {
        $uploadDesc = "";
        $arrChunk = array();
        $arrRes = array();
        $nLenght = empty($p_nFileSize) ? filesize($p_strFilePathName) : $p_nFileSize;
        $strTempFileName = uniqid('KMSTORE_', true);
        $strMD5 = md5_file($p_strFilePathName);
        $arrChunk = self::splitByFilePath($p_strFilePathName, $nLenght);
        $nChunks = count($arrChunk);
        $uploadDesc.="total ".$nChunks." Chunks;";
        for ($i = 0; $i < $nChunks; $i++) {
            $uploadDesc.=$i." Chunk start;";
            //上传失败重试10次
            for($j=0; $j<10; $j++) {
                $arrRes = self::uploadChunk($p_strUrl, $nChunks, $i, $arrChunk[$i], $strMD5, $p_nFileType, $p_strFileExt, $p_strFileName);
                if (!isset($arrRes['errorcode'])) {
                    $uploadDesc.= "curl empty,retry ".$j." start;";
                    continue;
                }
                if(in_array($arrRes['errorcode'],[-1,0,10])) {
                    $uploadDesc.= "success,errorcode".$arrRes['errorcode'].";";
                    break;
                }
            }
            if ($arrRes['errorcode'] != 0) {
                break;
            } else {
                //$i=$arrRes['chunk'];
            }
        }
        if (!empty($arrRes['fileid']) && ($arrRes['fileid'] > 0)) {
            $arrRes['desc'] = $uploadDesc;
            return $arrRes;
        } else {
            return false;
        }
    }

    /**
     * 使用文件路径切分为多个分块文件，未验证
     * @param string $p_strFilePathName
     * @param string $p_nLength
     * @return array
     */
    public static function splitByFilePath($p_strFilePathName = "", $p_nLength = "") {
        $buffer = '';
        $nReadSize = 4096;
        $nBuffer = 0;
        $nChunk = 0;
        $arrChunk = array();

        $strFilePathName = $p_strFilePathName;
        $handle = @fopen($strFilePathName, "r");
        while (!feof($handle)) {
            $buffer .= @fread($handle, $nReadSize);
            $nBuffer+=$nReadSize;
            if ($nBuffer >= self::MAX_CHUNK_SIZE) {
                $fname = $strFilePathName . ".part.$nChunk";
                self::writeFile($fname, $buffer);
                $arrChunk[] = $fname;
                $nChunk++;
                $nBuffer = 0;
                //unset($buffer);
                $buffer = '';
            }
        }
        if (!empty($buffer)) {
            $fname = $strFilePathName . ".part.$nChunk";
            self::writeFile($fname, $buffer);
            $arrChunk[] = $fname;
            //unset($buffer);
            $buffer = '';
        }
        fclose($handle);
        //TODO 上传后立即删除，不做备份
        @unlink($p_strFilePathName);
        return $arrChunk;
    }

    /**
     * 读取文件
     * @param $fname
     * @param $buffer
     * @return bool
     */
    public static function writeFile($fname, $buffer) {
        if (!$fhandle = @fopen($fname, 'wb')) {
            return false;
        }

        if (!@fwrite($fhandle, $buffer)) {
            return false;
        }
        fclose($fhandle);
        return true;
    }

}
